<!DOCTYPE html>
<html>
<head>
  <title>Test ICE Servers</title>
  <meta charset="utf-8" />
  <meta http-equiv="X-UA-Compatible" content="IE=edge" />
  <meta name="viewport" content="width=device-width, initial-scale=1.0" />
</head>

<body>
  <h1>Test ICE Servers</h1>
  <hr />
  <p id="ip"></p>
  <p id="stun">🔴 The STUN server is NOT reachable!</p>
  <p id="turn">🔴 The TURN server is NOT reachable!</p>
  <p id="err"></p>
  <hr />
  <script>
    const IP = document.getElementById('ip');
    const Stun = document.getElementById('stun');
    const Turn = document.getElementById('turn');
    const Err = document.getElementById('err');

    document.addEventListener("DOMContentLoaded", main);

    async function main() {
      const token = await _getToken()
      const payload = await _decodeJwt(token);
      const iceServers = [
        {
          urls: `stun:${window.location.hostname}:3478`
        },
        {
          urls: `turn:${window.location.hostname}:3478`,
          username: payload.username,
          credential: payload.credential
        }
      ];

      // Print iceServers config
      console.log("ICE Servers", iceServers)

      // Create a new RTCPeerConnection using the STUN and TURN servers
      const pc = new RTCPeerConnection({ iceServers });

      // Handle each ICE candidate as it is discovered.
      pc.onicecandidate = (e) => {
        // Ignore the final empty candidate (null means gathering finished)
        if (!e.candidate || e.candidate.candidate === '') return;

        // Show candidate
        console.log(e.candidate.candidate);

        // Detect and indicate if a STUN candidate is found and gather the Public IP address
        if (e.candidate.type == 'srflx' || e.candidate.candidate.includes('srflx')) {
          let ip = /\b\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}\b/;
          let address = e.candidate.address 
            ? e.candidate.address 
            : e.candidate.candidate.match(ip);
          IP.innerHTML = '🟢 Your Public IP Address is ' + address;
          Stun.innerHTML = '🟢 The STUN server is reachable!';
        }

        // Detect and indicate if a TURN (relay) candidate is found
        if (e.candidate.type == 'relay' || e.candidate.candidate.includes('relay')) {
          Turn.innerHTML = '🟢 The TURN server is reachable!';
        }
      };

      // Close the connection when ICE gathering is complete.
      pc.onicegatheringstatechange = () => {
        if (pc.iceGatheringState === 'complete') {
          pc.close();
        }
      };

      // Handle any ICE candidate errors.
      pc.onicecandidateerror = (e) => {
        console.error(e);
      };

      // Create a dummy data channel (required to initiate ICE gathering)
      pc.createDataChannel('test');

      // Start ICE gathering by creating and setting a local offer
      pc.createOffer().then(offer => pc.setLocalDescription(offer));
    }

    // Fetches a temporary token (JWT) from the server.
    async function _getToken() {
      try {
        // Request the credentials from the server API
        const response = await fetch('/api/credentials', { method: "GET" });

        // Check if the response is successful (HTTP 2xx)
        if (!response.ok) {
          throw new Error(`Failed to fetch token: ${response.status} ${response.statusText}`);
        }

        // Parse the response as JSON
        const data = await response.json();

        // Extract the token from the response
        const token = data?.token;
        if (!token) {
          throw new Error('No token found in server response.');
        }

        // Return token
        return token;
      } 
      catch (err) {
        // Log and rethrow for higher-level error handling
        console.error('Error fetching token:', err);
      }
    }

    // Decodes the payload of a JWT
    async function _decodeJwt(token) {
      const payload = token.split(".")[1];
      const json = atob(payload);
      return JSON.parse(json);
    }
  </script>
</body>
</html>
